Skip to content

Bump React Native 0.88 - #101739

Open
collectioneur wants to merge 43 commits into
Expensify:mainfrom
software-mansion-labs:collectioneur/bump-rn-0.88
Open

collectioneur wants to merge 43 commits into
Expensify:mainfrom
software-mansion-labs:collectioneur/bump-rn-0.88

Conversation

@collectioneur

@collectioneur collectioneur commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Explanation of Change

Upgrades React Native 0.86 to 0.88 and Expo SDK 57 to 58 (with React 19.3, Metro 0.87 and Hermes 260318099). Ports our patches to the new versions, drops the ones fixed upstream, adds RN 0.88 build fixes for some native libraries, and adapts StatusBar, the web asset registry, types and Jest to the new APIs.

Non-obvious package.json changes:

  • @react-native/assets-registry is now a direct dependency. RN 0.87+ moved the registry into core and no longer depends on this package, but react-native-svg still imports @react-native/assets-registry/registry without declaring it. The App does not import it, so it is in knip's ignoreDependencies.
  • react-freeze is now a direct dependency. The App imports it in FreezeWrapper and ScreenFreezeWrapper but only got it transitively through react-native-screens, which knip flags.
  • The expo-location patch (Guard failed lastLocation task in expo-location to stop Android crash on revoked location permission #101091) and its buildFromSource entry are removed. expo-location 58 reads lastLocation with addOnSuccessListener/addOnFailureListener instead of reading task.result in addOnCompleteListener, so the crash after revoking the location permission is fixed upstream, and the prebuilt AAR can be used again. Test 6 steps 5-6 cover it.

Patches

This PR adds five small compatibility patches instead of bumping those libraries, because no bump removes them safely: two libraries have no release with the fix, and for the other three the fixed version changes much more than the few-line patch it would replace. Each details.md records the version that lets us drop the patch, and the bumps can follow as separate PRs once RN 0.88 is merged.

  • react-native-draggable-flatlist (new 004): RN 0.87 removed InteractionManager, but the library still calls InteractionManager.runAfterInteractions(reset) during render when the list keys change (Columns search, adding or removing waypoints). The render throws, React recovers, and reset() never runs, so the drag state goes stale. The patch uses requestAnimationFrame instead, as the upstream fix does. No fixed release: upstream issue #627 and PR #628 are open, and 4.0.3 (May 2025) is still the latest version.
  • react-native-advanced-input-mask (new 001): RN 0.87 removed the React/RCTBaseTextInputView.h header, which the library imports without using, so the iOS build fails. The patch drops the import. No fixed release: upstream PR #155 is open, and 1.4.6 is still the latest version.
  • react-native-plaid-link-sdk (new 002): in RN 0.88 getCurrentActivity() is a plain Kotlin function, so PlaidModule.kt does not compile. The patch reads reactApplicationContext.currentActivity. Fixed in 12.7.0, bump separately: our OAuth patch 001 (bank redirects in HybridApp on iOS) does not apply to 12.7.0 or 12.8.8 and has to be re-ported and tested against a real bank, and the bump also moves the native Plaid SDKs (Android sdk-core 5.3.4 to 5.5.x).
  • @ua/react-native-airship (new 001): RN 0.88 changed the Kotlin signatures of ViewManagerDelegate, so four overrides fail with ACCIDENTAL_OVERRIDE. The patch suppresses that diagnostic. Fixed in 27.0.0, bump separately: 26.12.0 fixes only one of the four overrides, and 27.0.0 is a major that moves AirshipFrameworkProxy from CocoaPods to Swift Package Manager on iOS, which needs its own push notification testing.
  • @react-native-documents/picker (new 001): same getCurrentActivity() change as Plaid. Fixed in 12.0.0, bump separately: that is two majors from 10.1.3 (11.0.0 drops CommonJS, 12.0.0 moves iOS to Swift 6 concurrency).

Fixed Issues

$ #101427
PROPOSAL: N/A

MOBILE-EXPENSIFY: https://github.com/Expensify/Mobile-Expensify/pull/14136

Tests

Tests are grouped by the platforms they run on, and steps that apply to only some of those platforms are marked. Most of the changes are native (React Native, Expo modules and their patches), so most tests are for the HybridApp on iOS and Android.

All platforms (iOS, Android, mWeb Safari, mWeb Chrome, desktop Chrome/Safari)

Test 1: The composer keeps live markdown and the patched TextInput behavior

  1. Open a chat and type *bold* _italic_ ~strike~ `code` , then on a new line > quote https://expensify.com @ followed by the first letters of a teammate's name, and pick them from the suggestions
  2. Verify that the markdown is formatted while you type
  3. Send the message
  4. Verify that the composer is cleared, and that the message shows bold, italic, strikethrough, the quote, the link, the mention, and code as an inline code block with a rounded border and padding around the text
  5. Native only: copy an image to the clipboard (iOS: long-press an image in Photos and pick Copy; Android: long-press an image in Chrome and pick Copy image), long-press the composer and pick Paste
  6. Verify that the image is sent to the chat as an attachment

Test 2: Receipts can be scanned, rotated and cropped

  1. Tap + > Create expense > Scan and take a photo of a receipt with the camera
  2. Tap + > Create expense > Scan again and pick a photo from the gallery. iOS only: pick an HDR photo taken with the iPhone camera (HEIC)
  3. Verify that both receipt previews show the correct orientation and the HDR photo is not black
  4. Finish creating the expense, open its receipt and use Rotate, then Crop
  5. Verify that the receipt rotates by 90 degrees and the crop is saved
  6. mWeb Safari only: repeat steps 4 and 5 with a receipt photo larger than 4096 px on a side, and verify that the rotated receipt is not blank

Test 3: Attachments open, zoom and play

  1. In a chat, tap + > Add attachment and pick a PDF from the device files (the file picker on web)
  2. Verify that the PDF uploads and opens in the attachment view
  3. Open an image attachment, pinch to zoom (click the image on web), then swipe or arrow to the next attachment
  4. Verify that zoom and paging work
  5. Open a video attachment, then play and pause it several times in a row
  6. Verify that the video plays and no error appears

Web only (desktop Chrome/Safari, mWeb Safari, mWeb Chrome)

Test 4: Web resolves images and icons through the new asset registry

  1. Open a chat that has image attachments and an expense with a receipt
  2. Verify that avatars, icons, the image attachments and the receipt thumbnail all render

Native only (iOS and Android HybridApp)

Test 5: The HybridApp starts and loads on React Native 0.88

  1. Build and launch the HybridApp with npm run ios and npm run android
  2. Sign in with an account that has chats and expenses
  3. Verify that the app gets past the splash screen and the Inbox shows your chats
  4. Switch between the Home, Inbox, Spend, Insights and Workspaces tabs, open Account from your avatar, then open a chat and go back (iOS: with the swipe-back gesture, Android: with the system Back button)
  5. Verify that each tab and the chat open without a crash or a red screen

Test 6: GPS distance tracking works with Expo 58

  1. Tap + > Track distance > GPS, tap Start and grant location access (Android: pick "While using the app", then "Allow all the time" on the Settings page the app opens)
  2. Tap Start, move at least a few hundred meters (or simulate a route in the simulator/emulator), then tap Stop
  3. Verify that the total distance is shown, then tap Save and finish creating the expense
  4. Verify that the distance expense is created with that distance and a map of the route
  5. Android only: start a new GPS trip, revoke the app's location permission in Android Settings while it tracks, then return to the app
  6. Verify that the app does not crash

Test 7: Native libraries with RN 0.88 build fixes still work

  1. Go to Account > Wallet > Add bank account > Get reimbursed, pick United States, then pick "Log into your bank"
  2. Verify that Plaid Link opens
  3. Go to Spend > Filters > Amount and type 1234.567 in the first amount field
  4. Verify that the input shows 1234.56 (at most two decimals)

Android only

Test 8: The chat keeps its scroll position when older messages load

  1. Open a chat with more than 100 messages
  2. Scroll up slowly until older messages load
  3. Verify that the message you were looking at stays in place and the list does not jump

Test 9: The status bar follows the screen

  1. Open a chat, open an image attachment, then close it
  2. Switch the theme in Account > Preferences > Theme between Dark and Light (pick a theme, then tap Save) and repeat step 1
  3. Verify that the app does not crash and that the status bar icons stay readable over each screen in both themes
  • Verify that no errors appear in the JS console

Offline tests

  1. Turn off the network
  2. Send a chat message and create a Scan expense with a receipt picked from the gallery
  3. Verify that the message and the expense appear in the pending (greyed out) state
  4. Turn the network back on
  5. Verify that the message is sent, the receipt uploads, and the pending state is cleared

QA Steps

Same as tests, using the staging HybridApp builds and staging web instead of the local npm run builds.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified any copy / text that was added to the app is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If the PR adds or modifies the UI:
    • I asked an AI agent to review the changes for accessibility issues and addressed its findings.
    • I tested with a screen reader (VoiceOver on macOS) and verified all new/changed elements are reachable with a logical focus order.
    • I verified all new/changed elements have meaningful accessible names and roles.
    • I verified state changes are announced (e.g. checked/unchecked, expanded/collapsed, selected).
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native

Test 1:

test1-composer-markdown-paste.mp4

Test 2:

test2a-gallery-scan-orientation-rotate-crop.mp4

Test 3:

test3a-pdf-upload-open-zoom.mp4
test3b-image-zoom-paging-video.mp4

Test 5:

test5-app-start-tabs.mp4

Test 6:

test6a-gps-permission-flow.mp4
test6b-gps-trip-save-offline-map-revoke-permission.mp4

Test 7:

test7-plaid-amount-input.mp4

Test 8:

test8-chat-scroll-older-messages.mp4

Test 9:

test9-status-bar-theme.mp4

Offline:

offline-test-2x.mp4
Android: mWeb Chrome
mweb-chrome-compressed.mp4
iOS: Native Test 1:
ScreenRecording_11-06-2026.11-54-09_1.MP4
ScreenRecording_11-06-2026.11-55-31_1.MP4

Test 2:

ScreenRecording_11-06-2026.12-02-39_1.MP4

Test 3:

ScreenRecording_11-06-2026.12-04-15_1.MP4

Test 5:

ScreenRecording_11-06-2026.12-05-13_1.MP4

Test 6:

ScreenRecording_11-06-2026.12-09-05_1_compressed.mp4

Test 7:

ScreenRecording_11-06-2026.03-13-15_1.MP4

Offline:

iOS: mWeb Safari
safari-mweb-offline-compressed.mp4
safari-mweb-compressed.mp4
MacOS: Chrome / Safari
webrn-compressed.mp4

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR is possibly changing native code and/or updating libraries, it may cause problems with HybridApp. Please check if any patch updates are required in the HybridApp repo and run an AdHoc build to verify that HybridApp will not break. Ask Contributor Plus for help if you are not sure how to handle this. ⚠️

…rn-0.88

# Conflicts:
#	package-lock.json
#	package.json
#	patches/@expensify/react-native-live-markdown/details.md
#	patches/@shopify/react-native-skia/details.md
# Conflicts:
#	package-lock.json
#	package.json
#	patches/react-native/react-native+0.88.0+017+publish-gradle.patch
@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/components/AmountTextInput.tsx 100.00% <100.00%> (ø)
src/components/CopyTextToClipboard.tsx 100.00% <ø> (ø)
...ckground/updateStatusBarAppearance/index.native.ts 75.00% <ø> (ø)
src/components/NumberWithSymbolForm.tsx 94.31% <100.00%> (ø)
.../components/Pressable/PressableWithDelayToggle.tsx 98.41% <ø> (ø)
src/components/TimePicker/TimePicker.tsx 46.15% <100.00%> (ø)
src/components/ValidateCodeInput.tsx 90.00% <100.00%> (+0.03%) ⬆️
src/libs/StatusBar/index.ts 100.00% <ø> (ø)
src/libs/StatusBar/types.ts 100.00% <ø> (ø)
src/components/FlatList/hooks/useFlatListHandle.ts 53.57% <0.00%> (ø)
... and 1 more
... and 9 files with indirect coverage changes

Both releases ship the RN 0.88 / AGP 9 fixes upstream, so the blob-util
ProGuard patch and the rnmapbox rn088-compat patch are dropped. The
rnmapbox immediate-follow-camera patch is ported to 10.3.10.
…rn-0.88

# Conflicts:
#	metro.config.js
#	package-lock.json
#	package.json
#	patches/metro/details.md
#	patches/metro/metro+0.87.1.patch
#	patches/react-native/details.md
#	src/components/ValidateCodeInput.tsx
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@joekaufmanexpensify
joekaufmanexpensify removed their request for review November 6, 2026 19:55
Comment thread android/gradle.properties Resolved
Comment thread android/build.gradle

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB / potential follow-up: I wonder if we should consider migrating our gradle scripts from Groovy to Kotlin

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed it'd be nice, it would pair well with moving off the legacy variant API so we can enable android.newDsl. Should I create an issue?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep I'd say go for it. We talked about doing it in a follow-up during the last RN upgrade and didn't

Comment thread android/gradle.properties Resolved
Comment thread config/rsbuild/rsbuild.common.ts Outdated
// Required for @shopify/react-native-skia web support
'react-native/Libraries/Image/AssetRegistry': false,
// RN 0.87+ dropped the @react-native/assets-registry package and moved the registry into core
// (react-native/asset-registry, a Flow file). expo-asset/expo-image import the new path, react-native-svg

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe we should fix this with a patch/upstream PR for react-native-svg?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's already an upstream PR in the repo, but it isn't merged yet. I'll ask the maintainers when they plan to fix this. I also added a comment with a link to the upstream issue, so we don't forget to remove these lines. So I think it would be better to leave it as is, without a patch, and just bump the library once they ship the release. WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Let's open a follow-up issue to upgrade react-native-svg and remove this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakex7 prepared and merged a fix for react-native-svg and will publish a new version soon 🎉

"Function invocation 'currentActivity()' expected". Read `reactApplicationContext.currentActivity` instead.
```

- Upstream PR/issue: Fixed upstream from 12.0.0 (11.x still has the bare `currentActivity`). Drop this patch when bumping to >= 12.0.0 (11.0.0 stops shipping CommonJS, 12.0.0 moves iOS to Swift 6 concurrency).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything blocking us from upgrading to upgrading to @react-native-documents/picker today?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing's blocking it, but it's a jump of two major versions. Maybe we could do this bump in a follow-up with proper QA? That way we won't hold up the RN upgrade

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, 👎🏼

I'm not willing to add a patch just to expedite completion of the React Native upgrade when there's nothing blocking us from just upgrading the other dependency instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll bump it in a separate PR before we land this one

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are we till using the base VirtualizedList components from React Native? cc @chrispader can't wait to get rid of the last of these in #98994

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked this patch, and it turns out the changes we made aren't needed anymore, since all the places that needed it have been migrated to FlashList. I'll test the patch removal a bit more with the other virtualized lists, but it looks like we can remove it without consequences

Comment thread patches/expo/details.md Outdated
```

- Upstream PR/issue: https://github.com/expo/expo/pull/43864
- Upstream PR/issue: https://github.com/expo/expo/pull/43864 (same change, closed without merging). Still unfixed in 58.0.14.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bumped @war-in to reopen that PR. Pretty lame that Expo just closed it "to keep the queue clear" even though they never actioned it at all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we don't need that PR anymore 🎉 we switched to Re.Pack so that path is not reachable for us anymore

@MelvinBot prepare an issue for the patch removal

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created Remove obsolete Expo HMR race-condition patch after Re.Pack migration, including documentation cleanup and verification criteria. No dependency on the upstream Expo PR.


view run

…ndroid.uniquePackageNames

litert-support is an empty wrapper that declares the same namespace as litert-support-api, so resolving it directly keeps AGP 9's namespace check on for every other library. Also explain which libraries keep android.builtInKotlin and android.newDsl off.
Every list that uses onStartReached is on FlashList now, which doesn't use VirtualizedList.
@collectioneur

Copy link
Copy Markdown
Contributor Author

@MelvinBot please create 2 follow-up issues, held on this PR:

  1. Bump react-native-svg and drop the @react-native/assets-registry workaround
  2. Migrate the Android Gradle build scripts from Groovy to Kotlin DSL

@MelvinBot

Copy link
Copy Markdown
Contributor

⚠️ A setup step failed before I could start. This is usually a temporary issue — try again in a few minutes, and if it persists post in #melvinbot-ai.

Run details

@kacper-mikolajczak

Copy link
Copy Markdown
Contributor

Hi! We are aware Melvin tester runs are failing. Please if you could update this branch to latest main and re-run it - this should resolve the issue. If not, reach out to me directly. Thanks for your patience!

@collectioneur

Copy link
Copy Markdown
Contributor Author

@MelvinBot please create 2 follow-up issues, held on this PR:

Bump react-native-svg and drop the @react-native/assets-registry workaround
Migrate the Android Gradle build scripts from Groovy to Kotlin DSL

@MelvinBot

Copy link
Copy Markdown
Contributor

⚠️ A setup step failed before I could start. This is usually a temporary issue — try again in a few minutes, and if it persists post in #melvinbot-ai.

Run details

@collectioneur

Copy link
Copy Markdown
Contributor Author

@MelvinBot please create 2 follow-up issues, held on this PR:

Bump react-native-svg and drop the @react-native/assets-registry workaround
Migrate the Android Gradle build scripts from Groovy to Kotlin DSL

@MelvinBot

Copy link
Copy Markdown
Contributor

⚠️ A setup step failed before I could start. This is usually a temporary issue — try again in a few minutes, and if it persists post in #melvinbot-ai.

Run details

…rn-0.88

# Conflicts:
#	Mobile-Expensify
#	ios/Podfile.lock
#	package-lock.json
#	package.json
#	patches/react-native/details.md
#	patches/react-native/react-native+0.88.0+001+fixMVCPAndroid.patch
#	patches/react-native/react-native+0.88.0+021+fix-hermes-sampling-profiler-pthread-kill-crash.patch
@collectioneur

Copy link
Copy Markdown
Contributor Author

@shubham1206agra could you please review this PR?

@kacper-mikolajczak

Copy link
Copy Markdown
Contributor

@MelvinBot create 2 follow-up issues as maintainer requests.

  • react-native-svg bump + @react-native/assets-registry workaround removal
  • Gradle build scripts migration from Groovy to Kotlin DSL

@kacper-mikolajczak

kacper-mikolajczak commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Hi @collectioneur 👋

I see melvin still failing here - I am investigating the issue and the re-run of mine will help me get to the bottom of things.

@collectioneur

Copy link
Copy Markdown
Contributor Author

Thank you!

@MelvinBot

Copy link
Copy Markdown
Contributor

Created both maintainer-request follow-ups, labeled Task, Monthly, and HOLD pending this PR’s merge:


view run

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants